#!/usr/bin/evn python
#encoding=utf-8
# main program
# edit yuanyun 2011/11/01
import os
import include
from include import *
from sys import argv
from struct import *
class colors :
    OKBLUE = '\033[94m'
    OKGREEN = '\033[92m'
    ENDC = '\033[0m'
    FAIL = '\033[91m'
    def disable(self):
        self.OKBLUE = ''
        self.OKGREEN = ''
        self.FAIL = ''
class pasre_args(object):
	def __init__(self, argv):
		self.argv = argv
		self.parms = {}
		self.tablespace = ''
	def parse_cmdline(self):
		argv = self.argv
		if len(argv) <= 1:
			print colors.FAIL + """\
	Usage: python inpage.py [OPTIONS] tablespace_file
	For more options , use python inpage.py -h""" + colors.ENDC
			return 0
		for args in argv :
			args = args.lstrip().rstrip()
			if args[:2] == '-h':
				self.parms[ args[:2] ] = ''
			elif args[:2] == '-v':
				self.parms[ args[:2] ] = ''
			elif args[:2] == '-s':
				self.parms[ args[:2] ] = args[3:]
			else :
				self.tablespace = args
		if self.parms.has_key('-h'):
			print colors.FAIL + """\
Get InnoDB Page Info
Usage: python inpage.py [OPTIONS] tablespace_file
The following options may be given as the first argument:
-s=list  Offset location data it skip display all specified page list look page content
   Support B+tree
-v     verbose mode if v and s do display pos list per page detail
-h     help
python inpage.py ibdata -v 
python inpage.py ibdata -v -s=pos,pos\
""" + colors.ENDC
			return 0
		return 1
def read_offset(page,offset,length) :
	ret = page[ offset:offset+length ]
	return ret.encode('hex')
def parse_offset( ret ) :
	ofs = []
	postition = 0
	tmp_ofs = ""
	for i in ret :
		if i == ',' :
			if postition == 0 :
				continue
			ofs.append( tmp_ofs )
			tmp_ofs = ""
		else :
			if i == ' ' :
				continue
			postition = 1
			tmp_ofs += i
	ofs.append( tmp_ofs )
	return ofs
def hex2bin( string ) :
	ret = int(string.upper(), 16)
	base = [str(x) for x in range(10)] + [ chr(x) for x in range(ord('A'),ord('A')+6)]
	mid = []
	while True :
		if ret == 0 :
			break
		ret,rem = divmod(ret, 2)
		mid.append(base[rem])
	return ''.join([str(x) for x in mid[::-1]])
def parse_row_ft( page ) :
	ft = read_offset( page , PAGE_N_HEAP , dif['PAGE_N_HEAP'] )
	ft = hex2bin( ft )
	if int(ft[1:],2) == 1 :
		return "Compact Or latest"
	else :
		return "REDUNDANT Or old"
def parse_fil( page ) :
	ret = {}
	ret['page_type'] = read_offset( page , FIL_PAGE_TYPE , dif['FIL_PAGE_TYPE'] )
	ret['tbsp_no'] = read_offset( page , FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID , dif['FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID'] )
	ret['check_num'] = read_offset( page , FIL_PAGE_SPACE_OR_CHKSUM , dif['FIL_PAGE_SPACE_OR_CHKSUM'] )
	ret['offset'] = read_offset( page , FIL_PAGE_OFFSET , dif['FIL_PAGE_OFFSET'] )
	ret['prev'] = read_offset( page , FIL_PAGE_PREV , dif['FIL_PAGE_PREV'] )
	ret['next'] = read_offset( page , FIL_PAGE_NEXT , dif['FIL_PAGE_NEXT'] )
	ret['lsn'] = read_offset( page , FIL_PAGE_LSN , dif['FIL_PAGE_LSN'] )
	ret['flsn'] = read_offset( page , FIL_PAGE_FILE_FLUSH_LSN , dif['FIL_PAGE_FILE_FLUSH_LSN'] )
	return ret
def parse_btr( page ) :
	ret = {}
	ret['slot'] = int( read_offset( page , PAGE_N_DIR_SLOTS , dif['PAGE_N_DIR_SLOTS'] ) , 16 )
	ret['recs'] = int( read_offset( page , PAGE_N_RECS , dif['PAGE_N_RECS'] ) , 16 )
	ret['level'] = int( read_offset( page , PAGE_LEVEL , dif['PAGE_LEVEL'] ) , 16 )
	ret['idx_no'] = read_offset( page , PAGE_INDEX_ID , dif['PAGE_INDEX_ID'] )
	ret['row_format'] = parse_row_ft( page ) # row format
	return ret
#def parse_content( page ) :
#	ret = None
	# 1 get slot last 2byte
#	fst_ofst = read_offset( page , REC_N_NEW_EXTRA_BYTES + 3 , dif['REC_N_NEW_EXTRA_BYTES'] )
def parse_pages( f , fn_loop ,args ) :
	#f.seek(0,0)
	ofs = []
	ofs = parse_offset( args.parms['-s'] )
	page_ret={}
	for i in xrange( fn_loop ) :
		pos = i * INNODB_PAGE_SIZE
		page = f.read( INNODB_PAGE_SIZE )
		fil_ret = parse_fil( page )
		for oofs in ofs :
			if str(pos) == str(oofs) :
				prt = 'pos %s offset %s tbsp %s prev %s next %s lsn %s flh_lsn %s check_num %s type %s' % ( pos , fil_ret['offset'] , fil_ret['tbsp_no'] ,  fil_ret['prev'] , fil_ret['next'] , fil_ret['lsn'] , fil_ret['flsn'] , fil_ret['check_num'] , innodb_page_type[fil_ret['page_type']] )
				if fil_ret['page_type'] in ( '45bf' , '000a' , '000b' ) :
					page_ret = parse_btr( page )
					print colors.OKBLUE + prt + ' idx_no %s slot %s row %s level %s' % ( page_ret['idx_no'] , page_ret['slot'] , page_ret['recs'] , page_ret['level'] ) + colors.ENDC
					if args.parms.has_key('-v') :
						for slots in xrange( 0 , page_ret['slot'] ) :
							feller_pos = FIRST_DIR_INFIMUN - slots
							slot_offset = int( read_offset( page , feller_pos - dif['INFIMUN_POS'] , dif['INFIMUN_POS'] ) , 16 )
							print colors.OKBLUE + 'slot %d page offset %d' % ( slots , slot_offset ) + colors.ENDC
						
				else :
					print colors.OKBLUE + prt + colors.ENDC
	return
def main() :
	args = pasre_args( argv )
	if args.parse_cmdline() == 0:
		return 2
	try :
		f = file( args.tablespace , 'rb' )
	except Exception , e :
		print e
		return 2
	fsize = os.path.getsize( f.name ) / INNODB_PAGE_SIZE
	if args.parms.has_key('-s') :
		if len( args.parms['-s'] ) < 1 :
			print bcolors.FAIL + 'Please Check Paraments -s=offset list' + bcolors.ENDC
			if f :
				f.close()
			return 2
		parse_pages( f , fsize ,args )
		if f :
			f.close()
		return 0
	ret = {}
	#f.seek(0,0)
	pos=0
	slot = 0
	rows=0
	for i in xrange( fsize ) :
		page = f.read( INNODB_PAGE_SIZE )
		pos = i * INNODB_PAGE_SIZE
		fil_ret = parse_fil( page )
		page_ret={}
		if fil_ret['page_type'] in ( '45bf' , '000a' , '000b' ) :
			page_ret = parse_btr( page )
			slot += page_ret['slot']
			rows += page_ret['recs']
		if args.parms.has_key('-v') :
			if fil_ret['page_type'] in ( '45bf' , '000b' , '000a' ) :
				print colors.OKGREEN + 'pos %s offset %s tbsp %s idxno %s prev %s next %s slot %s row %s level %s type %s' % ( pos , fil_ret['offset'] , fil_ret['tbsp_no'] , page_ret['idx_no'] , fil_ret['prev'] , fil_ret['next'] , page_ret['slot'] , page_ret['recs'] , page_ret['level'] , innodb_page_type[fil_ret['page_type']] ) + colors.ENDC
			else :
				print colors.OKGREEN + 'pos %d offset %s type %s' % ( pos , fil_ret['offset'] , innodb_page_type[fil_ret['page_type']] ) + colors.ENDC
		if ret.has_key( fil_ret['page_type'] ) :
			ret[fil_ret['page_type']] += 1
		else :
			ret[fil_ret['page_type']] = 1
	if f :
		f.close()
	print colors.OKBLUE + 'Total of page %d %dm slot %d row %d' % ( fsize , os.path.getsize( f.name ) / 1024 / 1024 , slot , rows ) + colors.ENDC
	for i in ret :
		print colors.OKBLUE +  '%s : %s size %f MB' % ( innodb_page_type[i] , ret[i] , (int(ret[i])*16384.0)/1048576.0 ) + colors.ENDC
if __name__ == '__main__' :
	main()
